import React, { useState, useEffect, useLayoutEffect, useRef } from "react";
import styles from "./test.less";
import { uuid } from "./util";
const defaultList_1 = [];
for (let index = 0; index < 10; index++) {
  defaultList_1.push({ id: `1-${uuid()}`, index: `1-${index}` });
}
const defaultList_2 = [];
for (let index = 0; index < 10; index++) {
  defaultList_2.push({ id: `2-${uuid()}`, index: `2-${index}` });
}
function Sortable({
  id,
  data,
  sort = true,
  put = false,
  pull = false,
  onChange,
}) {
  const listRef = useRef();
  const itemsRef = useRef({});
  const currentRef = useRef(null);
  const cloneRef = useRef(null);
  const nodes = useRef([]);
  const pos = useRef({});
  const newItem = useRef(null);
  const currentNode = useRef(null);
  const isCopy = useRef(false);

  const [list, setList] = useState(data);

  const mousedownListener = (e) => {
    e.stopPropagation();
    pos.current = { x: e.pageX, y: e.pageY };
    let $dom = null;
    for (let i = 0; i < nodes.current.length; i++) {
      let item = nodes.current[i];
      if (item?.dataset?.id === e?.target?.dataset?.id) {
        $dom = item;
      }
    }

    if (e.target === $dom) {
      currentRef.current = $dom;
      currentRef.current.nodeRect = currentRef?.current.getBoundingClientRect();
    }
  };
  const initHelper = (cloneNode) => {
    cloneRef.current = listRef.current.appendChild(cloneNode);
    cloneRef.current.style.position = "fixed";
    cloneRef.current.style.top = currentRef.current.nodeRect.top + "px";
    cloneRef.current.style.left = currentRef.current.nodeRect.left + "px";
    cloneRef.current.style.width = currentRef.current.nodeRect.width + "px";
    cloneRef.current.style.height = currentRef.current.nodeRect.height + "px";
  };

  const swapArr = (arr, index1, index2) => {
    arr[index1] = arr.splice(index2, 1, arr[index1])[0];
    return arr;
  };
  const getRect = (dom) => {
    return dom.getBoundingClientRect();
  };
  const checkReact = (domA, domB) => {
    const reactA = getRect(domA);
    const reactB = getRect(domB);
    return !(
      reactA.x + reactA.width / 2 < reactB.x ||
      reactB.x + reactB.width / 2 < reactA.x ||
      reactA.y + reactA.height / 2 < reactB.y ||
      reactB.y + reactB.height / 2 < reactA.y
    );
  };
  const css = (el, prop, val) => {
    let style = el && el.style;

    if (style) {
      if (val === void 0) {
        if (document.defaultView && document.defaultView.getComputedStyle) {
          val = document.defaultView.getComputedStyle(el, "");
        } else if (el.currentStyle) {
          val = el.currentStyle;
        }

        return prop === void 0 ? val : val[prop];
      } else {
        if (!(prop in style) && prop.indexOf("webkit") === -1) {
          prop = "-webkit-" + prop;
        }

        style[prop] = val + (typeof val === "string" ? "" : "px");
      }
    }
  };
  const setNodes = () => {
    let _nodes = Object.values(itemsRef.current);
    if (_nodes.length === list.length) {
      nodes.current = _nodes;
    }
  };
  const getNodes = () => {
    return nodes.current;
  };
  const repaint = (target) => {
    return target.offsetWidth;
  };
  const swapElements = (a, b) => {
    if (a === b) return;
    //记录父元素
    var bp = b.parentNode,
      ap = a.parentNode;
    //记录下一个同级元素
    var an = a.nextElementSibling,
      bn = b.nextElementSibling;
    //如果参照物是邻近元素则直接调整位置
    if (an === b) return bp.insertBefore(b, a);
    if (bn === a) return ap.insertBefore(a, b);
    if (a.contains(b))
      //如果a包含了b
      return ap.insertBefore(b, a), bp.insertBefore(a, bn);
    else return bp.insertBefore(a, b), ap.insertBefore(b, an);
  };
  const animate = (target, transitionY) => {
    css(target, "transform", "");
    css(target, "transition", "");
    css(target, "transform", `translate3d(0px,${transitionY}px,0)`);

    repaint(target);

    css(target, "transition", "transform 0.15s");
    css(target, "transform", `translate3d(0px,0px,0)`);
    if (typeof target.animated === "number") {
      clearTimeout(target.animated);
      css(target, "transform", "");
      css(target, "transition", "");
      target.isActivity = false;
    }
    target.timerOut = setTimeout(() => {
      css(target, "transform", "");
      css(target, "transition", "");
      target.isActivity = false;
    }, 0.15 * 1000);
  };
  const move = () => {
    if (sort) {
      let _nodes = getNodes();
      for (let i = 0; i < _nodes.length; i++) {
        let item = _nodes[i];

        const index = _nodes.findIndex((item) => item === currentRef.current);
        if (index === i) {
          newItem.current = item;
          newItem.current.index = i;
          continue;
        }

        if (checkReact(item, cloneRef.current) && item.isActivity != true) {
          item.isActivity = true;
          swapElements(item, newItem.current);
          console.log(i, newItem.current.index);
          let newArray = swapArr(nodes.current, i, newItem.current.index);
          nodes.current = newArray;
          const newItemRact = newItem.current.getBoundingClientRect();
          const currentNodeRact = item.getBoundingClientRect();
          const moveY = newItemRact.top - currentNodeRact.top;
          animate(item, moveY);
          animate(newItem.current, -moveY);
        }
      }
    }
  };
  const copymove = (_cloneRef) => {
    let _nodes = getNodes();
    for (let i = 0; i < _nodes.length; i++) {
      let item = _nodes[i];

      if (checkReact(item, _cloneRef) && item.isActivity != true) {
        if (!isCopy.current) {
          isCopy.current = true;
          let newDom = _cloneRef.cloneNode(true);
          newDom.style = "";
          listRef.current && listRef.current.insertBefore(newDom, item);
          newItem.current = newDom;
          newItem.current.style.opacity = 0;
          newItem.current.style.visibility = "hidden";
          const newIndex = i > 0 ? i - 1 : 0;
          newItem.current.index = newIndex;
          newItem.current.info = { item: _cloneRef.data, index: newIndex };
        }
        nodes.current.splice(i > 0 ? i - 1 : 0, 0, newItem.current);

        if (checkReact(item, _cloneRef) && item.isActivity != true) {
          item.isActivity = true;
          swapElements(item, newItem.current);
          let newArray = swapArr(nodes.current, i, newItem.current.index);
          nodes.current = newArray;
          newItem.current.index = i;
          newItem.current.info = { item: _cloneRef.data, index: i };
          const newItemRact = newItem.current.getBoundingClientRect();
          const currentNodeRact = item.getBoundingClientRect();
          const moveY = newItemRact.top - currentNodeRact.top;
          animate(item, moveY);
          animate(newItem.current, -moveY);
          console.log(id, i, newItem.current.index);
        }
      }
    }
  };
  const mousemoveListener = (e) => {
    const { x, y } = pos.current;
    let mouseMove = {
      x: e.pageX - x,
      y: e.pageY - y,
    };
    if (put && e.target.isClone && id === "list1") {
      console.log(id, "mousemoveListener");
      copymove(e.target);
      return;
    }

    if (!currentRef?.current) return;

    if (Math.abs(mouseMove.x) > 5 || Math.abs(mouseMove.y) > 5) {
      if (!cloneRef?.current) {
        let cloneNode = currentRef.current.cloneNode(true);
        const index = list.findIndex(
          (item) => item.id === currentRef.current.dataset.id
        );
        cloneNode.data = {
          ...list[index],
          id: `${list[index]["id"]}-${uuid()}`,
        };

        if (pull) {
          cloneNode.isClone = true;
        } else {
          currentRef.current.style.opacity = 0;
          currentRef.current.style.visibility = "hidden";
        }

        initHelper(cloneNode);
      } else {
        move();
        cloneRef.current.style.transform = `translate3d(${mouseMove.x}px,${mouseMove.y}px,0)`;
      }
    }
  };

  const removeNode = (node) => {
    if (node?.parentElement !== null) {
      node.parentElement.removeChild(node);
    }
  };
  const getList = () => {
    let arr = [];
    for (let i = 0; i < nodes.current.length; i++) {
      let _item = nodes.current[i];
      arr.push(_item?.info?.item);
    }
    setList([...arr]);
  };
  const mouseupListener = (e) => {
    if (newItem.current && cloneRef?.current) {
      if (isCopy.current) {
        console.log(cloneRef?.current);
        if (checkReact(listRef.current, cloneRef.current)) {
          getList();
        } else {
          setNodes();
        }
        removeNode(newItem.current);
      }
      isCopy.current = false;
    }
    if (cloneRef?.current) {
      removeNode(cloneRef.current);

      cloneRef.current = null;
    }
    if (currentRef?.current) {
      currentRef.current.style = "";
      currentRef.current = null;
      newItem.current = null;
      currentNode.current = null;
      getList();
    }
  };

  useEffect(() => {
    onChange && onChange(list);
  }, [list]);
  useEffect(() => {
    const $dom = listRef?.current;
    if ($dom) {
      document.addEventListener("mousedown", mousedownListener);
      document.addEventListener("mousemove", mousemoveListener);
      document.addEventListener("mouseup", mouseupListener);
    }
    return () => {
      if ($dom) {
        document.removeEventListener("mousedown", mousedownListener);
        document.removeEventListener("mousemove", mousemoveListener);
        document.removeEventListener("mouseup", mouseupListener);
      }
    };
  });

  const setItemsRef = (ref, item, index) => {
    if (ref) {
      itemsRef.current[index] = ref;
      itemsRef.current[index]["info"] = {
        item,
        index,
      };

      setNodes();
    }
  };
  return (
    <div>
      <div draggable="false" ref={listRef} className={styles.test}>
        {list.map((item, index) => (
          <div
            data-id={`${item?.id}`}
            ref={(ref) => setItemsRef(ref, item, index)}
            key={item?.id}
            className={styles.box}
          >
            {item?.index}
          </div>
        ))}
      </div>
    </div>
  );
}
function Index() {
  return (
    <div className={styles.list}>
      <Sortable put={true} id={"list1"} data={defaultList_1} />
      <Sortable pull={true} sort={false} id={"list2"} data={defaultList_2} />
    </div>
  );
}
export default Index;
